import {
    BufferGeometry,
    Geometry,
    Line,
    Matrix3,
    Mesh,
    Vector2,
    Vector3
} from "three";

var OBJExporter = function () {};

OBJExporter.prototype = {

    constructor: OBJExporter,

    parse: function ( object ) {

        var output = '';

        var indexVertex = 0;
        var indexVertexUvs = 0;
        var indexNormals = 0;

        var vertex = new Vector3();
        var normal = new Vector3();
        var uv = new Vector2();

        var i, j, k, l, m, face = [];

        var parseMesh = function ( mesh ) {

            var nbVertex = 0;
            var nbNormals = 0;
            var nbVertexUvs = 0;

            var geometry = mesh.geometry;

            var normalMatrixWorld = new Matrix3();

            if ( geometry instanceof Geometry ) {

                geometry = new BufferGeometry().setFromObject( mesh );

            }

            if ( geometry instanceof BufferGeometry ) {

                // shortcuts
                var vertices = geometry.getAttribute( 'position' );
                var normals = geometry.getAttribute( 'normal' );
                var uvs = geometry.getAttribute( 'uv' );
                var indices = geometry.getIndex();

                // name of the mesh object
                output += 'o ' + mesh.name + '\n';

                // name of the mesh material
                if ( mesh.material && mesh.material.name ) {

                    output += 'usemtl ' + mesh.material.name + '\n';

                }

                // vertices

                if ( vertices !== undefined ) {

                    for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {

                        vertex.x = vertices.getX( i );
                        vertex.y = vertices.getY( i );
                        vertex.z = vertices.getZ( i );

                        // transform the vertex to world space
                        vertex.applyMatrix4( mesh.matrixWorld );

                        // transform the vertex to export format
                        output += 'v ' + vertex.x.toFixed(2) + ' ' + vertex.y.toFixed(2) + ' ' + vertex.z.toFixed(2) + '\n';

                    }

                }

                // uvs

                if ( uvs !== undefined ) {

                    for ( i = 0, l = uvs.count; i < l; i ++, nbVertexUvs ++ ) {

                        uv.x = uvs.getX( i );
                        uv.y = uvs.getY( i );

                        // transform the uv to export format
                        output += 'vt ' + uv.x + ' ' + uv.y + '\n';

                    }

                }

                // normals

                if ( normals !== undefined ) {

                    normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );

                    for ( i = 0, l = normals.count; i < l; i ++, nbNormals ++ ) {

                        normal.x = normals.getX( i );
                        normal.y = normals.getY( i );
                        normal.z = normals.getZ( i );

                        // transform the normal to world space
                        normal.applyMatrix3( normalMatrixWorld ).normalize();

                        // transform the normal to export format
                        output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';

                    }

                }

                // faces

                if ( indices !== null ) {

                    for ( i = 0, l = indices.count; i < l; i += 3 ) {

                        for ( m = 0; m < 3; m ++ ) {

                            j = indices.getX( i + m ) + 1;

                            face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );

                        }

                        // transform the face to export format
                        output += 'f ' + face.join( ' ' ) + "\n";

                    }

                } else {

                    for ( i = 0, l = vertices.count; i < l; i += 3 ) {

                        for ( m = 0; m < 3; m ++ ) {

                            j = i + m + 1;

                            face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );

                        }

                        // transform the face to export format
                        output += 'f ' + face.join( ' ' ) + "\n";

                    }

                }

            } else {

                console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', geometry );

            }

            // update index
            indexVertex += nbVertex;
            indexVertexUvs += nbVertexUvs;
            indexNormals += nbNormals;

        };

        var parseLine = function ( line ) {

            var nbVertex = 0;

            var geometry = line.geometry;
            var type = line.type;

            if ( geometry instanceof Geometry ) {

                geometry = new BufferGeometry().setFromObject( line );

            }

            if ( geometry instanceof BufferGeometry ) {

                // shortcuts
                var vertices = geometry.getAttribute( 'position' );

                // name of the line object
                output += 'o ' + line.name + '\n';

                if ( vertices !== undefined ) {

                    for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {

                        vertex.x = vertices.getX( i );
                        vertex.y = vertices.getY( i );
                        vertex.z = vertices.getZ( i );

                        // transform the vertex to world space
                        vertex.applyMatrix4( line.matrixWorld );

                        // transform the vertex to export format
                        output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';

                    }

                }

                if ( type === 'Line' ) {

                    output += 'l ';

                    for ( j = 1, l = vertices.count; j <= l; j ++ ) {

                        output += ( indexVertex + j ) + ' ';

                    }

                    output += '\n';

                }

                if ( type === 'LineSegments' ) {

                    for ( j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) {

                        output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n';

                    }

                }

            } else {

                console.warn( 'THREE.OBJExporter.parseLine(): geometry type unsupported', geometry );

            }

            // update index
            indexVertex += nbVertex;

        };

        object.traverse( function ( child ) {

            if ( child instanceof Mesh ) {

                parseMesh( child );

            }

            if ( child instanceof Line ) {

                parseLine( child );

            }

        } );

        return output;

    }

};

export { OBJExporter };
